package com.neusoft.neuiotms.dcvm.service.impl;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import javax.persistence.EntityNotFoundException;

import org.apache.commons.collections4.map.HashedMap;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.neusoft.bizcore.web.constant.ExceptionConstants;
import com.neusoft.bizcore.web.exception.UnitedException;
import com.neusoft.bizcore.web.support.Searchable;
import com.neusoft.neuiotms.dcvm.converter.ModelConverter;
import com.neusoft.neuiotms.dcvm.dto.FileDTO;
import com.neusoft.neuiotms.dcvm.dto.ModelDTO;
import com.neusoft.neuiotms.dcvm.dto.ModelVO;
import com.neusoft.neuiotms.dcvm.dto.ModelVersionDTO;
import com.neusoft.neuiotms.dcvm.model.Model;
import com.neusoft.neuiotms.dcvm.repository.ModelRepository;
import com.neusoft.neuiotms.dcvm.service.FileService;
import com.neusoft.neuiotms.dcvm.service.ModelService;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class ModelServiceImpl implements ModelService {

    @Autowired
    private ModelConverter modelConverter;
    @Autowired
    private ModelRepository modelRepository;
    @Autowired
    private FileService fileService;

    @Override
    public List<ModelDTO> findAll() {
        final List<Model> models = this.modelRepository.findAll();
        return this.modelConverter.toListDTO(models);
    }

    @Override
    public ModelDTO details(Long id) {
        Model model;
        try {
            model = this.modelRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }
        return this.modelConverter.toDTO(model);
    }

    @Override
    public ModelDTO create(ModelDTO dto) {
        Model model = this.modelRepository.findByModelCode(dto.getModelCode());
        if (null == model) {
            model = this.modelConverter.toModel(dto);
        } else {
            dto.setId(model.getId());
            model = this.modelConverter.toModel(dto);
        }

        this.modelRepository.save(model);
        return this.modelConverter.toDTO(model);
    }

    @Override
    public ModelDTO update(Long id, ModelDTO dto) {
        Model model;
        try {
            model = this.modelRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }

        dto.setId(model.getId());
        model = this.modelConverter.toModel(dto);
        this.modelRepository.save(model);

        return this.modelConverter.toDTO(model);
    }

    @Override
    public void delete(Long id) {
        try {
            this.modelRepository.deleteById(id);
        } catch (final EmptyResultDataAccessException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }
    }

    @Override
    public Page<ModelDTO> search(Pageable pageable, Searchable searchable) {
        final Page<Model> models = this.modelRepository.search(pageable, searchable);
        return this.modelConverter.toPageDTO(models);
    }

    @Override
    public int upload(MultipartFile file) {
        final AtomicInteger count = new AtomicInteger();
        final String packageName = file.getOriginalFilename();
        if (packageName.matches(".*\\.zip")) {
            try {
                final ZipInputStream zs = new ZipInputStream(file.getInputStream());
                final BufferedInputStream bs = new BufferedInputStream(zs);
                ZipEntry ze;
                List<ModelVO> importList = new ArrayList<>();
                //预览图 图片文件名:文件内容
                final Map<String, byte[]> previews = new HashedMap<>();
                //小地图 图片文件名:文件内容
                final Map<String, byte[]> maps = new HashedMap<>();
                // 模具定义
                final Map<String, List<Version>> models = new HashedMap<>();

                while ((ze = zs.getNextEntry()) != null) {
                    if (!ze.isDirectory()) {
                        final String[] segments = ze.getName().split("/");
                        final String fileName = segments[segments.length - 1];
                        final String dirName = segments[segments.length - 2];
                        final byte[] data = new byte[(int) ze.getSize()];
                        bs.read(data, 0, (int) ze.getSize());

                        ModelServiceImpl.log.info("{}:{}", fileName, data.length);

                        if ("ModelInfo.xlsx".equals(fileName)) {
                            final ImportParams params = new ImportParams();
                            params.setHeadRows(1);
                            params.setNeedVerify(true);

                            ExcelImportResult<ModelVO> result = null;
                            try {
                                result = ExcelImportUtil.importExcelMore(new ByteArrayInputStream(data), ModelVO.class,
                                        params);
                            } catch (final Exception e) {
                                ModelServiceImpl.log.error(e.getMessage(), e);
                                throw new UnitedException("导入失败", e);
                            }

                            if ((result != null) && result.isVerifyFail()) {
                                final List<ModelVO> failList = result.getFailList();
                                final StringBuilder sb = new StringBuilder();

                                failList.forEach(l -> {
                                    sb.append("第" + (l.getRowNum() + 1) + "行," + l.getErrorMsg() + "! \n");
                                });

                                throw new UnitedException(sb.toString());

                            }

                            importList = result.getList();
                            importList.forEach(vo -> {
                                if (vo.getTypeId().indexOf("_") > 0) {
                                    vo.setTypeId(vo.getTypeId().split("_")[0]);
                                }
                            });

                        } else if ("previews".equals(dirName)) {
                            previews.put(fileName, data);
                        } else if ("maps".equals(dirName)) {
                            maps.put(fileName, data);
                        } else {
                            final String versionCode = dirName;
                            final String modelCode = fileName.substring(0, fileName.lastIndexOf("."));

                            final Version v = new Version();
                            v.setFileName(fileName);
                            v.setVersion(versionCode);
                            v.setContent(data);
                            if (!models.containsKey(modelCode)) {
                                final List<Version> versions = new ArrayList<>();
                                versions.add(v);
                                models.put(modelCode, versions);
                            } else {
                                models.get(modelCode).add(v);
                            }
                        }

                    }

                }

                importList.forEach(it -> {
                    final String modelCode = it.getModelCode();
                    //if (this.modelRepository.countByModelCode(modelCode) == 0) {
                    if (models.containsKey(modelCode)) {
                        final ModelDTO dto = this.modelConverter.toDTO(it);
                        if ((null != it.getPreview())
                                && previews.containsKey(StringUtils.trim(it.getPreview()))) {
                            final FileDTO previewDTO =
                                    this.fileService.save(previews.get(StringUtils.trim(it.getPreview())),
                                            StringUtils.trim(it.getPreview()), "image/png");
                            dto.setImgUrl(previewDTO.getFsId());
                        }
                        if ((null != it.getMap())
                                && maps.containsKey(StringUtils.trim(it.getMap()))) {
                            final FileDTO mapDTO =
                                    this.fileService.save(maps.get(StringUtils.trim(it.getMap())),
                                            StringUtils.trim(it.getMap()), "image/png");
                            dto.setMapUrl(mapDTO.getFsId());
                        }
                        final List<Version> versions = models.get(modelCode);
                        for (final Version entry : versions) {
                            final FileDTO modelFileDTO =
                                    this.fileService.save(entry.getContent(), entry.getFileName(),
                                            "application/octet-stream");
                            final ModelVersionDTO versionDTO = new ModelVersionDTO();
                            versionDTO.setVersionCode(entry.getVersion());
                            versionDTO.setFileName(entry.getFileName());
                            versionDTO.setFileUrl(modelFileDTO.getFsId());
                            dto.getVersionList().add(versionDTO);
                        }
                        try {
                            this.create(dto);
                            count.getAndIncrement();
                        } catch (final Exception e) {
                            ModelServiceImpl.log.error("", e);
                        }
                    }
                    //}
                });
            } catch (final IOException e) {
                ModelServiceImpl.log.error("", e);
            }
        }

        return count.get();
    }

    @Getter
    @Setter
    static class Version {
        String version;
        byte[] content;
        String fileName;
    }

}
